#-*- coding: utf-8 -*-

import helpers
import functools

# zmienna modułowa zawierająca wszystkie strategie 
# dodane przez @Strategy i wygenerowane przez rozwinięcie
# parametrów z @ParametrizedStrategy
strategies = []

# TODO: dodać dekorator kontrstrategii

def Reversible(x):
    return x

i = 0 
def Strategy(sell_treshold, buy_treshold, *argumentsList): # {{{
    """ 
    Dekorator strategii

    Sygnatura strategii jest następująca:
    
        value = strategy(data)

    Gdzie value jest wartością z przedziału [0.0, 1.0],
    a data jest obiektem klasy StockModel zawierającym jakieś
    dane dotyczące spółki.

    Wartość zwracaną przez funkcję interpetuje się jako sygnał
    do akcji na giełdzie. 
    Wartości między 0 a sell_treshold włącznie oznaczają sprzedaż.
    Wartości między sell_treshold a buy_treshold oznaczają brak akcji.
    [sell_treshold, buy_treshold]
        
    Strategię definiuje się następująco:

        @Strategy(1./3, 1./3)
        def someStrategy(data):
            return random.random()

        @Strategy(1./3, 1./3, [1,2], [10,20,30])
        def someStrategy(param1, param2, data):
            return param1+param2*random.random()


    Dalsze argumenty dekoratora po granicach sygnałów oznaczają listy parametrów
    jakie funkcja może przejmować. 
    Kolejne grupy odpowiadają kolejnym argumentom funkcji. Dekorator nie zmienia sygnatury funkcji, 
    ale rozwija podane listy parmetrów, aplikując je częściowo do funkcji, 
    i dodając tak zmodifykowaną funkcję do listy `strategies',
    """

    def wrap(f):
        def newf(*args, **kwds):

            order = None

            result = f(*args, **kwds)

            if (result != None):
                if (result <= sell_treshold):
                    order = False   # Sprzedaj
                elif(result >= buy_treshold):
                    order = True    # Kup

            """
            return { "order": order
                   , "sell":  sell_treshold
                   , "raw":   result
                   , "buy":   buy_treshold
                   , "limit": None }
            """
            return order

        if len(argumentsList) == 0:
            strategies.append(newf)
        else:
            for argumentsGroup in argumentsList:
                for arguments in argumentsGroup:
                    if getattr(arguments, '__iter__', False):
                        strategies.append(functools.partial(newf, *arguments))
                    else:
                        strategies.append(functools.partial(newf, arguments))

        return newf

    return wrap
# }}}

@Strategy(0,1)
def alwaysBuy(data):
    return 1

@Strategy(0,1)
def alwaysSell(data):
    return 0

@Strategy(0,1)
def random(data):
    ret = helpers.binaryRandom(0.5)
    return ret

#@Strategy(0,1,[1, 3, 5, 7, 10, 15, 30, 45, 60, 90])
@Strategy(0,1,[1, 3, 15, 45])
def momentum(period, data):

    if data.currentDayIndex - period - 1 < data.firstDayIndex:
        return 0.5

    x1 = data.getMomentum(period)
    x2 = data.getMomentum(period, -1)
    if x1 >= 0 and x2 < 0:
        return 1
    elif x1 < 0 and x2 >= 0:
        return 0
    else:
        return 0.5

#@Strategy(0,1, [3, 5, 7, 10, 15, 30, 45, 60, 75, 90])
@Strategy(0,1, [3, 15, 45, 90])
def movingAverage(period, data):
    if data.currentDayIndex - period + 1 < data.firstDayIndex:
        return 0.5

    x1 = data.getSMA(period, -1)
    x2 = data.getSMA(period)
    y1 = data.getClosePrice(-1)
    y2 = data.closePrice

    if (x1 - y1) * (x2 - y2) <= 0:
        if x1 - y1 > 0:
            return 1
        elif x1 - y1 < 0:
            return 0
        else:
            return 0.5
    else:
        return 0.5

#@Strategy(0,1, [[2,3],[2, 5],[3, 7],[3, 10],[5,20],[7,15],[15,75],[30, 60],[45, 90],[60, 90]])
@Strategy(0,1, [[2, 5],[3, 10],[5,20],[30, 60],[60, 90]])
def twoMovingAverages(shortPeriod, longPeriod, data):

    if data.currentDayIndex - longPeriod - 1 < data.firstDayIndex:
        return 0.5

    x1 = data.getSMA(longPeriod, -1)
    x2 = data.getSMA(longPeriod)
    y1 = data.getSMA(shortPeriod, -1)
    y2 = data.getSMA(shortPeriod)

    if (x1 - y1) * (x2 - y2) <= 0:
        if x1 - y1 > 0:
            return 1
        elif x1 - y1 < 0:
            return 0
        else:
            return 0.5
    else:
        return 0.5


#@Strategy(0,1, [3, 5, 7, 10, 15, 30, 45, 60, 75, 90])
@Strategy(0,1, [3, 10, 45])
def movingExpAverage(period, data):
    if data.currentDayIndex - period - 1 < data.firstDayIndex:
        return 0.5

    x1 = data.getEMA(period, -1)
    x2 = data.getEMA(period)
    y1 = data.getClosePrice(-1)
    y2 = data.closePrice

    if (x1 - y1) * (x2 - y2) <= 0:
        if x1 - y1 > 0:
            return 1
        elif x1 - y1 < 0:
            return 0
        else:
            return 0.5
    else:
        return 0.5


#@Strategy(0,1, [[2,3],[2, 5],[3, 7],[3, 10],[5,20],[7,15],[15,75],[30, 60],[45, 90],[60, 90]])
@Strategy(0,1, [[2, 5],[15,75],[45, 90]])
def twoMovingExpAverages(shortPeriod, longPeriod, data):

    if data.currentDayIndex - longPeriod - 1 < data.firstDayIndex:
        return 0.5

    x1 = data.getEMA(longPeriod, -1)
    x2 = data.getEMA(longPeriod)
    y1 = data.getEMA(shortPeriod, -1)
    y2 = data.getEMA(shortPeriod)

    if (x1 - y1) * (x2 - y2) <= 0:
        if x1 - y1 > 0:
            return 1
        elif x1 - y1 < 0:
            return 0
        else:
            return 0.5
    else:
        return 0.5

#@Strategy(0, 1, [14, 15, 30, 60, 90])
@Strategy(0, 1, [14, 30, 90])
def crossoverDirectionalRule(period, data):
    if data.currentDayIndex - period - 1 < data.firstDayIndex:
        return 0.5

    pdi = data.getPDI(period)
    ndi = data.getNDI(period)

    pdi1 = data.getPDI(period, -1)
    ndi1 = data.getNDI(period, -1)

    if pdi > ndi and pdi1 <= ndi1:
        return 1

    if ndi > pdi and ndi1 <= pdi1:
        return 0


@Strategy(0, 1, [14, 15, 30, 60, 90])
def averageDirectionalMovement(period, data):
    # TODO: pomyśleć o tym trochę dłużej
    if data.currentDayIndex - 2*period - 1 < data.firstDayIndex:
        return 0.5

    adx = data.getADX(period)

    # to tylko jeden filtr
    pdm = data.getPDM()
    ndm = data.getNDM()

    if (adx > 40 and (pdm != 0 or ndm >= 0.15)):
        return 0

    if (adx > 30 and pdm != 0 and ndm >= 0.15):
        return 0

    return 0.5

@Strategy(0, 1, [14, 15, 30, 60, 90])
def averageDirectionalMovementMomentumOscilator(period, data):
    if data.currentDayIndex - 2*period - 1 < data.firstDayIndex:
        return 0.5

    adx = data.getADX(period)
    momentum  = data.getMomentum(period)
    momentum1 = data.getMomentum(period, -5)

    if adx < 40 and momentum >= 0 and momentum1 < 0:
        return 0

    if adx >= 40 and momentum >= 0 and momentum1 >= 0:
        return 1

    # TODO: gdy adx odbija od 20 przy niezerowym trendzie, kupuj

    return 0.5


#@Strategy(0, 1, helpers.cross([10000, 20000, 40000, 60000, 80000, 100000], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
@Strategy(0, 1, helpers.cross([10000, 80000], [2, 5, 10]))
def accumulationDistribution(step, num, data):
    limit = step * num
    licznik = data.getClosePrice() - data.getOpenPrice()
    mianownik = data.getHighPrice() - data.getLowPrice()

    if mianownik == 0:
        return 0.5

    if (licznik / mianownik) * data.getVolume() > limit:
        return 1

    if -(licznik / mianownik) * data.getVolume() > limit:
        return 0

    return 0.5

#@Strategy(0, 1, helpers.cross([10000, 20000, 40000, 60000, 80000, 100000], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
@Strategy(0, 1, helpers.cross([10000, 80000], [2, 5, 10]))
def obv(step, num, data):
   limit = step * num
   if abs(data.OBV) < limit:
       return 0.5

   if data.OBV > 0:
       return 1

   return 0

#@Strategy(0, 1, helpers.cross([0.1,0,2,0.3,0.4,0.5], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
@Strategy(0, 1, helpers.cross([0.1, 0.5], [2, 5, 10]))
def rateOfChangeVolume(treshold, period, data):
    if data.currentDayIndex - period  < data.firstDayIndex:
        return 0.5

    result = data.getROV(period)

    if result < -treshold:
       return 0

    if result > treshold:
       return 1

    return 0.5

#@Reversible
#@Strategy(0, 1, helpers.cross([0.1,0,2,0.3,0.4,0.5], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
@Strategy(0, 1, helpers.cross([0.1, 0.5], [2, 5, 10]))
def rateOfChangePrice(treshold, period, data):
    if data.currentDayIndex - period  < data.firstDayIndex:
        return 0.5

    result = data.getROC(period)

    if result < -treshold:
       return 0

    if result > treshold:
       return 1

    return 0.5


#@Strategy(0, 1, helpers.cross([1.5, 2.0, 2.5], [30, 40, 50, 60, 90]))
@Strategy(0, 1, helpers.cross([1.5, 2.5], [30, 60]))
def realchange(num, delta, data):
    """ Brak sklonnosci do ryzyka """
    if data.currentDayIndex - delta  < data.firstDayIndex:
        return 0.5

    result = data.getTR(delta)
    if result < delta:
        return 1

    if result < num * delta:
        return 0.5

    return 0

#@Strategy(0, 1, helpers.cross([ 0.6, 0.7, 0.8, 0.9], [14, 15, 16, 17, 18, 19, 20]))
@Strategy(0, 1, helpers.cross([0.6, 0.9], [14, 20]))
def oscilator(limit, days, data):

    if data.currentDayIndex-days < 0:
        return 0.5

    result = data.getStochasticOscillator(days-14)
    if result < 0:
       return 0.5

    if result < limit:
        return 0

    return 1

if __name__ == '__main__':
    #print alwaysBuy(None)
    #print alwaysSell(None)

    print(len(strategies))
#

# vim: fdm=marker
